package com.hontech.desktoppastacooker.conn

import com.hontech.desktoppastacooker.conn.frame.ResFrame
import com.hontech.desktoppastacooker.conn.frame.ResultFrame
import com.hontech.desktoppastacooker.conn.serial.SerialType
import com.hontech.desktoppastacooker.conn.usb.UsbSerialPortMod
import com.hontech.desktoppastacooker.define.USB_NAME

import com.hontech.desktoppastacooker.utils.ConnTask
import com.hontech.desktoppastacooker.utils.PromiseTask
import com.hontech.desktoppastacooker.utils.await
import kotlinx.coroutines.CompletableDeferred
import java.io.IOException


typealias SerialPortTask = ConnTask

object SerialPortMod {

    private const val BAUD_RATE = 115200

    private var mPort: SerialPort? = null
    private var mReader: SerialPortReader? = null
    private var globalSeq = 0

    fun isOpen() = mReader != null

    suspend fun open(name: String) {
        mReader?.close()

        val port = if (name == USB_NAME) {
            UsbSerialPortMod.open()
        } else {
            TTYSerialPort(name, BAUD_RATE)
        }
        val reader = SerialPortReader(port)
        mPort = port
        mReader = reader
        globalSeq = 0
    }

    suspend fun close() {
        mReader?.close()
        mPort = null
        mReader = null
    }

    private fun getSeq(): Int {
        if (globalSeq > 255) {
            globalSeq = 0
        }
        val ret = globalSeq
        globalSeq ++
        return ret
    }

    suspend fun query(cmd: Int): ResultFrame {

        val port = mPort ?: throw IOException("串口没有打开")
        val reader = mReader ?: throw IOException("串口没有打开2")

        return PromiseTask {

            reader.queryBegin()
            val buf = Proto.makeQuery(cmd)
            port.writeWithLock(buf)
            reader.resultAwait()
        }.await(SerialPortTask)
    }

    private fun execRequest(port: SerialPort, reader: SerialPortReader, seq: Int, cmd: Int, args: Array<SerialType>): CompletableDeferred<ResFrame> {

        var index = 0
        val sessionBuf = Proto.makeSession(seq)
        val reqBuf = Proto.makeReq(seq, cmd, args)

        while (true) {
            try {
                reader.ackReset()
                port.writeWithLock(sessionBuf)
                reader.ackAwait()
                break
            } catch (e: Exception) {
                index ++
                if (index > 3) {
                    throw e
                } else {
                    e.printStackTrace()
                }
            }
        }

        index = 0
        while (true) {
            try {
                val promise = reader.reqBegin(seq, cmd)
                port.writeWithLock(reqBuf)
                reader.ackAwait()
                return promise
            } catch (e: Exception) {
                index ++
                if (index > 3) {
                    throw e
                } else {
                    e.printStackTrace()
                }
            }
        }
    }


    suspend fun request(timeout: Long, cmd: Int, args: Array<SerialType>): ResFrame {
        val seq = getSeq()
        val port = mPort ?: throw IOException("串口没有打开")
        val reader = mReader ?: throw IOException("串口没有打开2")
        return PromiseTask {
            execRequest(port, reader, seq, cmd, args)
        }.await(SerialPortTask).await(timeout)
    }
}





